﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Practices.Unity;
using JTLeigh.Commerce.Spencer.Core.Common;
using JTLeigh.Commerce.Spencer.Features.ProductFeedback.Business;
using JTLeigh.Commerce.Spencer.Features.ProductFeedback.Business.Services;

// aliases for entities
using ProductFeedbackE = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Common.Entities.ProductFeedback;
using LocalizedProductRatingAttributeE = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Common.Entities.LocalizedProductRatingAttribute;
using LocalizedProductRecommendationAttributeE = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Common.Entities.LocalizedProductRecommendationAttribute;
using ProductRatingAttributeValueE = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Common.Entities.ProductRatingAttributeValue;
using ProductRecommendationAttributeValueE = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Common.Entities.ProductRecommendationAttributeValue;

// aliases for contracts
using GetProductFeedbackSnapshotRequestC = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Service.Contracts.Runtime.GetProductFeedbackSnapshot.GetProductFeedbackSnapshotRequest;
using GetProductFeedbackSnapshotResponseC = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Service.Contracts.Runtime.GetProductFeedbackSnapshot.GetProductFeedbackSnapshotResponse;
using ProductFeedbackSnapshotC = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Service.Contracts.Runtime.GetProductFeedbackSnapshot.ProductFeedbackSnapshot;
using ProductRatingAttributeC = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Service.Contracts.Runtime.GetProductFeedbackSnapshot.ProductRatingAttribute;
using ProductRecommendationAttributeC = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Service.Contracts.Runtime.GetProductFeedbackSnapshot.ProductRecommendationAttribute;

namespace JTLeigh.Commerce.Spencer.Features.ProductFeedback.Service.Commands.Runtime
{

	/// <summary>
	/// Command for getting a snapshot of the feedback for a single product, including total and 
	/// average ratings, rating breakdowns, total and positive recommendations, total reviews, and
	/// relevant attribute metadata.
	/// </summary>
	public class GetProductFeedbackSnapshotCommand
	{

		#region Properties

		/// <summary>
		/// Gets or sets the product feedback metadata service to use to get product feedback 
		/// metadata.
		/// </summary>
		/// <value>An <see cref="IProductFeedbackMetadataService"/> implementation.</value>
		[Dependency]
		public IProductFeedbackMetadataService ProductFeedbackMetadataService { get; set; }

		/// <summary>
		/// Gets or sets the product feedback service to use to get product feedback.
		/// </summary>
		/// <value>An <see cref="IProductFeedbackRuntimeService"/> implementation.</value>
		[Dependency]
		public IProductFeedbackRuntimeService ProductFeedbackRuntimeService { get; set; }

		#endregion // Properties

		#region Methods

		/// <summary>
		/// Gets a snapshot of the feedback for a single product, including total and average 
		/// ratings, rating breakdowns, total and positive recommendations, total reviews, and
		/// relevant attribute metadata.
		/// </summary>
		/// <param name="request">The <see cref="GetProductFeedbackSnapshotRequestC"/> request 
		/// object.</param>
		/// <returns>The <see cref="GetProductFeedbackSnapshotResponseC"/> response object.</returns>
        [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Reviewed, but to be reviewed again later.")]
        public GetProductFeedbackSnapshotResponseC GetProductFeedbackSnapshot(GetProductFeedbackSnapshotRequestC request)
		{
			IList<ProductFeedbackE> feedback;
			Dictionary<Guid, LocalizedProductRatingAttributeE> ratingAttributes;
			Dictionary<Guid, LocalizedProductRecommendationAttributeE> recommendationAttributes;
			BreakdownAveragingCounter ratingCounter;
			AttributeAveragingCounter ratingAttributeCounter;
			BooleanCounter recommendationCounter;
			AttributeBooleanCounter recommendationAttributeCounter;
			int totalReviews;
			GetProductFeedbackSnapshotResponseC response;
			ProductRatingAttributeC ratingAttributeC;
			ProductRecommendationAttributeC recommendationAttributeC;

			Safety.AssertArgumentNotNull("request", request);

			feedback = ProductFeedbackRuntimeService.GetProductFeedback(request.TenantId, request.ProductId.CatalogId, request.ProductId.ProductId, request.SelectionBehaviorName);
			ratingAttributes = ProductFeedbackMetadataService.GetProductRatingAttributes(request.TenantId, request.ProductId.CatalogId, request.ProductId.ProductId, request.CultureId, request.EffectiveDate, feedback);
			recommendationAttributes = ProductFeedbackMetadataService.GetProductRecommendationAttributes(request.TenantId, request.ProductId.CatalogId, request.ProductId.ProductId, request.CultureId, request.EffectiveDate, feedback);

			ratingCounter = new BreakdownAveragingCounter();
			ratingAttributeCounter = new AttributeAveragingCounter();
			recommendationCounter = new BooleanCounter();
			recommendationAttributeCounter = new AttributeBooleanCounter();
			totalReviews = 0;

			foreach (ProductFeedbackE feedbackE in feedback)
			{
				if (feedbackE.Rating != null)
				{
					ratingCounter.AddValue(feedbackE.Rating.Value);
					foreach (ProductRatingAttributeValueE attributeValue in feedbackE.Rating.AttributeValues.Values)
					{
						ratingAttributeCounter.AddValue(attributeValue.AttributeId, attributeValue.Value);
					}
				}
				if (feedbackE.Recommendation != null)
				{
					recommendationCounter.AddValue(feedbackE.Recommendation.Value);
					foreach (ProductRecommendationAttributeValueE attributeValue in feedbackE.Recommendation.AttributeValues.Values)
					{
						recommendationAttributeCounter.AddValue(attributeValue.AttributeId, attributeValue.Value);
					}
				}
				if (feedbackE.Review != null)
				{
					totalReviews++;
				}
			}

			response = new GetProductFeedbackSnapshotResponseC
			{
				Snapshot = new ProductFeedbackSnapshotC
				{
					TotalRatings = ratingCounter.Count,
					AverageRatingValue = ratingCounter.Average,
					RatingValueBreakdown = ratingCounter.Breakdown,
					AverageRatingAttributeValues = ratingAttributeCounter.Averages,
					TotalRecommendations = recommendationCounter.Count,
					PositiveRecommendations = recommendationCounter.PositiveCount,
					TotalRecommendationAttributeValues = recommendationAttributeCounter.TotalCounts,
					PositiveRecommendationAttributeValues = recommendationAttributeCounter.PositiveCounts,
					TotalReviews = totalReviews,
				},
				RatingAttributes = new Dictionary<Guid, ProductRatingAttributeC>(),
				RecommendationAttributes = new Dictionary<Guid, ProductRecommendationAttributeC>(),
			};

			foreach (LocalizedProductRatingAttributeE ratingAttributeE in ratingAttributes.Values)
			{
				ratingAttributeC = new ProductRatingAttributeC
				{
					AttributeId = ratingAttributeE.AttributeId,
					DisplayOrder = ratingAttributeE.DisplayOrder,
					Text = ratingAttributeE.Text,
				};
				response.RatingAttributes.Add(ratingAttributeC.AttributeId, ratingAttributeC);
			}

			foreach (LocalizedProductRecommendationAttributeE recommendationAttributeE in recommendationAttributes.Values)
			{
				recommendationAttributeC = new ProductRecommendationAttributeC
				{
					AttributeId = recommendationAttributeE.AttributeId,
					DisplayOrder = recommendationAttributeE.DisplayOrder,
					Text = recommendationAttributeE.Text,
				};
				response.RecommendationAttributes.Add(recommendationAttributeC.AttributeId, recommendationAttributeC);
			}

			return response;
		}

		#endregion // Methods

	}

}
